-- 
-- Inventory Viewer by The Gooch.  All Rights Reserved.
-- 

-- modifiable variables
local g_IVStatus 	= {};				-- never save this info anywhere, it's session specific
local g_CharTabOrder			= {};
local g_AccountName 			= GetAccountName(); -- Should be safe to call this early in the load
local g_Realm 					= "";
local g_CurrentPlayerName		= "";
local g_MoveSrcCharId			= 0;
local g_PreviousVersion 		= 0;
local g_IVSelectedCharTabName 	= "";
local g_IVSelectedCharTabID 	= 0;

-- Can't rename this anymore as it's used in the SaveVariables.lua file
g_InventoryViewerTable					= {};		-- Save backpack/bank/chest info to file
g_InventoryViewerTable[g_AccountName] 	= {};

-- const defines
local PLUGIN_NAME 				= "Inventory Viewer"
local PLUGIN_VERSION 			= 1.4;
local PLUGIN_CREDIT				= "The Gooch";
local PLUGIN_TITLE 				= PLUGIN_NAME .. string.format(" v%.1f" , PLUGIN_VERSION).. " (by " ..PLUGIN_CREDIT .. ")";
local TABLE_DATA_FILENAME 		= "InventoryViewer(".. g_AccountName ..").ini";
local INTERNAL_SETTINGS_TABLE 	= "InternalSettings";
local IV_MAX_CHARS_PER_REALM	= 8;				-- replacing MAX_CHARACTERS_PER_REALM which is incorrectly set to 10

-- Function hooks
local orig_CloseAllWindows = CloseAllWindows;
local orig_GameTooltipHyperLinkSetHyperLink = GameTooltipHyperLink["SetHyperLink"];
local orig_GameTooltipSetHyperLink = GameTooltip["SetHyperLink"];
local orig_GameTooltipSetBagItem = GameTooltip["SetBagItem"];
local orig_GameTooltipSetBankItem = GameTooltip["SetBankItem"];
local orig_GameTooltipSetAuctionBrowseItem = GameTooltip["SetAuctionBrowseItem"];
local orig_GameTooltipSetBootyItem = GameTooltip["SetBootyItem"];
local orig_GameTooltipSetHouseItem = GameTooltip["SetHouseItem"];
local orig_GameTooltipSetStoreItem = GameTooltip["SetStoreItem"];

local APOSTRAPHE_S						= "'s";							-- Tooltip text
local TEXT_BANK							= "Bank";						-- Generic text for UI and tooltips
local TEXT_BACKPACK						= "Backpack";					-- Generic text for UI and tooltips
local TEXT_ITEMSHOP_BACKPACK			= "Item Shop Backpack";			-- Tooltip text
local TEXT_HOUSE_CHEST					= "House Chest (none) :";		-- Generic Text for our UI
local TEXT_CLOTHES_WEAPON_RACK			= "Clothes/Weapon Rack :";		-- Generic Text for our UI
local TEXT_CLOTHES_RACK					= "Clothes Rack";				-- 8 slots
local TEXT_WEAPON_RACK					= "Weapon Rack";				-- 2 slots

if ( GetLocation() == "DE" ) then
	APOSTRAPHE_S						= "s";							-- Tooltip text (no apostraphe for German client)
	TEXT_BANK							= "Bank";						-- Generic text for UI and tooltips
	TEXT_BACKPACK						= "Rucksack";					-- Generic text for UI and tooltips
	TEXT_ITEMSHOP_BACKPACK				= "Item-Shop Rucksack";			-- Tooltip text
	TEXT_HOUSE_CHEST					= "Truhe (nicht vorhanden) :";	-- Generic Text for our UI
	TEXT_CLOTHES_WEAPON_RACK			= "Kleider/Waffe Rack :";		-- Generic Text for our UI
	TEXT_CLOTHES_RACK					= "Gestell f\195\188r";			-- 8 slots
	TEXT_WEAPON_RACK					= "Waffenregal";				-- 2 slots
end

-- Localization
local CHEST_SEARCH_TABLE = { " Chest", } 						-- Just search for " Chest" since currently all Chests have <space>Chest in their name on English clients
local CHEST_SEARCH_TABLE_DE = { "truhe", 
								"Einfacher Vorratsschrank" }	-- Just search for "truhe" since currently all Chests have truhe or Truhe in their name on German clients (except the 1-slot Magnificent footstoll chest)

function IV_Log(msg, r,g,b)
	if (DEFAULT_CHAT_FRAME) then
		DEFAULT_CHAT_FRAME:AddMessage(msg, r,g,b);
	end
end


--
-- This function will strip all colors from the passed in text
--
function IV_StripColor( TextToStrip )

	local stripped = TextToStrip;

	while ( string.find( stripped, "|c" ) ~= nil ) do
		stripped = string.gsub( stripped, "|c%x%x%x%x%x%x%x%x", "" );
		stripped = string.gsub( stripped, "|r", "" );
	end

	return stripped;
end


function IV_IsInitialized()
	
	if ( g_IVStatus.Initialized == true ) then
		return true;
	end

	return false;
end


function IV_SetInitialized()

	g_IVStatus.Initialized = true;
	--IV_Log( "g_InventoryViewer initilaized." );
end


function IV_GetVersionFromOldDataFiles()

	local version = 0;
	
	local tmp = {};
	
	table.copy( tmp, table.load( GC_GetGlobalPath(TABLE_DATA_FILENAME) ) );
	
	if ( tmp[INTERNAL_SETTINGS_TABLE] and tmp[INTERNAL_SETTINGS_TABLE].Version ) then
		version = tmp[INTERNAL_SETTINGS_TABLE].Version;
	end
	
	tmp = nil;

	if ( version ~= 0 ) then
		--IV_Log( "Inventory viewer: Found old data files, version: " .. version );
	end
	
	return version;
end


function IV_HandleUpgrade()

	IV_Log( "Handling Inventory Viewer data upgrade from v" ..tostring(g_PreviousVersion).. " to v" ..tostring(PLUGIN_VERSION) );
	
	if ( g_PreviousVersion > PLUGIN_VERSION ) then

		IV_Log( "Inventory Viewer data on disk is newer than this version of Inventory Viewer.  This may cause unpredictable behavior.", 1,0,0 );
	
	elseif ( g_PreviousVersion < 0.4 ) then
		-- Ignore data that is too old and imcompatible with this version
	
	elseif ( g_PreviousVersion == 0.4 or g_PreviousVersion == 0.5 ) then
		
		-- Load the data from our old data file and save it in the SavedVariables.lua file.
		if ( not g_InventoryViewerTable[g_AccountName][g_Realm] ) then
		
			IV_Log( "Inventory Viewer is importing v" .. g_PreviousVersion .. " data." );
			IV_MoveDataFromFileToSavedVariables();

			-- Delete the previous House Chest now that we will be supporting multiple House Chests
			IV_Remove1Point1OrOlderChests();
		end
		
	elseif ( g_PreviousVersion >= 0.6 and g_PreviousVersion <= 1.1 ) then
	
		if ( g_PreviousVersion == 0.6 or g_PreviousVersion == 0.7 ) then
		
			-- Delete the Hanger tables for each server/character on this account
			-- Unfortunately, due to some bug, 0.7 users will still show a version of 0.6 in the SaveVariables.lua file.
			IV_Remove07HangerTables();
		end
		
		-- Delete the previous House Chest now that we will be supporting multiple House Chests
		IV_Remove1Point1OrOlderChests();
	
	else -- Previous build?
	
	end
end


function IV_IsTabOrderNumTaken( orderNum )

	-- Cycle through known chars on this server to see if they have this order number yet
	for CharName,junk in pairs(g_InventoryViewerTable[g_AccountName][g_Realm]) do

		if ( CharName and CharName ~= "Chest" ) then
		
			if ( g_InventoryViewerTable[g_AccountName][g_Realm][CharName].DisplayOrder ) then
				
				if ( orderNum == g_InventoryViewerTable[g_AccountName][g_Realm][CharName].DisplayOrder ) then
					-- This number is taken
					return true;
				end
			end
		end
	end
	
	-- this number is available
	return false;
end


function IV_SetCharTabNames()

	local MyTabId = 1;
	local NextAvailDisplayOrderNum = 1;	-- This number will be handed out to any Chars that don't have a DisplayOrder number (then incremented)

	if ( not g_CurrentPlayerName or g_CurrentPlayerName == "" ) then
		g_CurrentPlayerName = UnitName("player");
	end

	-- Figure out which characters have saved info and set our tabs as their names
	for CharName,CharTable in pairs( g_InventoryViewerTable[g_AccountName][g_Realm] ) do

		if ( CharName and CharName ~= "Chest" ) then

			-- Default to the NextAvailDisplayOrderNum (will be overridden if this character has it's own DisplayOrder)
			local DisplayOrder = NextAvailDisplayOrderNum;

			-- If this character already has a DisplayOrder number, use it instead
			if ( CharTable.DisplayOrder ) then
				DisplayOrder = CharTable.DisplayOrder;
				--IV_Log( CharName.. " order already set to: " ..DisplayOrder );
			else
				-- Give this character a DisplayOrder number since he doesn't have one yet (this will happen if new character or upgrading from older AddOn)
				-- Make sure this character gets a DisplayOrder that isn't already taken
				while ( IV_IsTabOrderNumTaken( NextAvailDisplayOrderNum ) ) do
					--IV_Log( NextAvailDisplayOrderNum .." is taken." );
					NextAvailDisplayOrderNum = NextAvailDisplayOrderNum + 1;
					
					-- protect against looping forever in case of bad data file
					if ( NextAvailDisplayOrderNum > 8 ) then
						IV_Log( "Inventory Viewer Error: All DisplayOrder numbers are taken!" );
						return;
					end
				end
				
				--IV_Log( "NextAvailDisplayOrderNum found: " .. NextAvailDisplayOrderNum );
				
				-- if we got this far, we have a new DisplayOrder number
				DisplayOrder = NextAvailDisplayOrderNum;
				
				--g_InventoryViewerTable[g_AccountName][g_Realm][CharName].DisplayOrder = DisplayOrder;
				CharTable.DisplayOrder = DisplayOrder;
				
				--IV_Log( "Setting " ..CharName.. " order to: " ..DisplayOrder );
			end

			local tab = getglobal("IVFrameCharTab"..DisplayOrder );

			-- Setthe Tab name and make sure it isn't hidden
			tab:SetText(CharName);
			tab:Show();	-- Our tabs are hidden by default, so we'll only show the ones that have names

			if ( CharName == g_CurrentPlayerName ) then

				MyTabId = DisplayOrder;
			end

			g_CharTabOrder[DisplayOrder] = CharName;
		end
	end

--	IV_LogTable( g_CharTabOrder );

	return MyTabId;
end


function IV_ToggleMinimapIcon()

	local isVisible = IV_MinimapButton:IsVisible();
	
	if ( isVisible ) then
		IV_MinimapButton:Hide();
		g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].EnableMinimapIcon = 0;
		IV_Log( "Inventory Viewer minimap frame icon is disabled.", 0, 0.4, 1 );
	else
		IV_MinimapButton:Show();
		g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].EnableMinimapIcon = 1;
		IV_Log( "Inventory Viewer minimap frame icon is enabled.  Right-click and drag it to your preferred location.", 0, 0.4, 1 );
	end
end


function IV_Initialize()
	--IV_Log("InventoryViewer initializing...");

	-- Bail if we're already initialized
	if ( IV_IsInitialized() == true ) then
		return;
	end

	-- Verify we have the right Account Name
	if ( g_AccountName ~= GetAccountName() ) then
		IV_Log( "Inventory Viewer Error: g_AccountName ~= GetAccountName()" .. g_AccountName .."~=".. GetAccountName(), 1,0,0 );
		g_AccountName = GetAccountName();
	end
	
	-- Set the Realm name once
	g_Realm = IV_StripColor( GetCurrentRealm() );
	
	if ( g_Realm == nil or g_Realm == "" ) then
		return;
	end

	-- Try to get the current player name.  If we can then we consider ourselves initialized
	g_CurrentPlayerName = UnitName("player");

	if ( g_CurrentPlayerName == nil or g_CurrentPlayerName == "" ) then
		return;
	end
	
	-- Initiaize our global data object if it isn't already
	if ( not g_InventoryViewerTable[g_AccountName] ) then
		g_InventoryViewerTable[g_AccountName] = {};
	end
	
	if ( not g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE] ) then 
		
		-- First run since 1812 or 1813 patch
		g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE] = {}; 
		g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].Version = PLUGIN_VERSION;
		--IV_Log( "Inventory viewer - Initialized Internal Settings Table" );
		
		-- Try to get the real version from the old data files, if any
		g_PreviousVersion = IV_GetVersionFromOldDataFiles();
		
		--Handle upgrade of data if necessary
		IV_HandleUpgrade();
		
	elseif ( g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].Version ) then

		g_PreviousVersion = tonumber(g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].Version) or 0;
		
		--Handle upgrade of data if necessary
		if ( g_PreviousVersion ~= PLUGIN_VERSION ) then
			IV_HandleUpgrade();
		end
	end		

	-- Set the new version number
	g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].Version = PLUGIN_VERSION;
	
	-- Update Tooltips with IV info by default.  Use "/iv Tooltips" to toggle it off/on
	if ( not g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].UpdateTooltips ) then
		g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].UpdateTooltips = 1;
	end

	if ( not g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].EnableMinimapIcon ) then
		g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].EnableMinimapIcon = 1;
	end

	if ( not g_InventoryViewerTable[g_AccountName][g_Realm] ) then 
		g_InventoryViewerTable[g_AccountName][g_Realm] = {}; 
	end
	
	if ( not g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName] ) then 
		g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName] = {}; 
	end

	if ( not g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Bank ) then 
		g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Bank = {}; 
	end

	if ( not g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Backpack ) then 
		g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Backpack = {}; 
	end

	if ( not g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].ItemShopBag ) then 
		g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].ItemShopBag = {}; 
	end

	if ( not g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Chest ) then 
		g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Chest = {}; 
	end
	

	-- Set flag saying we're initialized so following functions don't bail out
	IV_SetInitialized();
	
	-- Save off Backpack items after we initialize for the first time.
	IVFrame_SetTable_Backpack();
	
	-- Save off Item Shop Backpack items after we initialize for the first time.
	IVFrame_SetTable_ItemShopBag();
	
	-- Save off Bank items after we initialize for the first time.
	IVFrame_SetTable_Bank();
	
	-- Figure out which characters have saved info and set our tabs as their names
	local MyTabId = IV_SetCharTabNames();
	
	-- Write out our data to the INI file incase user quits
	IVFrame_SaveTable();

	-- Set the Tab to the current player, This will set the Bank Tab and Backpack tab to page 1.
	IVFrame_SelectCharTab( MyTabId );
	
	-- Populate the UI with the current players items  -- This is already done in the above call: IVFrame_SelectCharTab( MyTabId );

	-- Minimap icon defaults to enabled, disable (hide) it if the user doesn't want to see it.
	if ( g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].EnableMinimapIcon == 0 ) then
		IV_MinimapButton:Hide();
	end
	
	
	-- Register with Alleris's AddOn Manager if available
	if ( AddonManager ) then

		local addon = {
			name = PLUGIN_NAME,
			description = "Keeps track of where your items are.",
			icon = "Interface/AddOns/InventoryViewer/Textures/IV_normal.tga",
			category = "Inventory",
			configFrame = IVFrame,
			slashCommands = SLASH_InventoryViewer2,
			miniButton = IV_AOMButton,
			version = string.format("v%.1f" , PLUGIN_VERSION),
			author = PLUGIN_CREDIT,
		}
		
		-- New AddOn Manager
		if AddonManager.RegisterAddonTable then
		
			--IV_Log( "Registering with new AddOn Manager" );
			AddonManager.RegisterAddonTable(addon)
			
		-- Old AddOn Manager
		else
			--IV_Log( "Registering with old AddOn Manager" );
			AddonManager.RegisterAddon( PLUGIN_TITLE, 
										addon.description, 
										addon.icon, 
										addon.category, 
										addon.configFrame, 
										addon.slashCommands, 
										addon.miniButton );
		end
	else
		IV_Log( PLUGIN_TITLE .. " loaded.  Usage: /iv" );
	end
end		


SLASH_InventoryViewer1 = "/inventoryviewer";
SLASH_InventoryViewer2 = "/iv";
SlashCmdList["InventoryViewer"] = function (editBox, msg)

	-- No command means show the UI
	if ( not msg or msg == "" ) then
		ShowUIPanel(IVFrame);
		return;
	end
	
	-- Toggle the updating of Tooltips
	if ( "TOOLTIPS" == string.upper(msg) ) then
	
		if ( not g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].UpdateTooltips or
			 g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].UpdateTooltips == 0 ) then
			 
			IV_Log( "Inventory Viewer will update tooltips from now on.", 0,.70,0 );
			g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].UpdateTooltips = 1;
		else
			IV_Log( "Inventory Viewer will no longer update tooltips.", .70,0,0 );
			g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].UpdateTooltips = 0;
		end
		
		return;
	end

	-- Toggle MiniMap icon
	if ( "MINIMAP" == string.upper(msg) ) then

		IV_ToggleMinimapIcon();
		return;
	end
	
	-- Look up an itemlink and show where we have it
	IV_Log( IV_GetListOfItems( msg ) );
end


function IV_Remove1Point1OrOlderChests()

	--IV_Log( "begin IV_Remove1Point1OrOlderChests" );
	
	-- Cycle through realms we have saved in old data files
	for Realm,junk in pairs(g_InventoryViewerTable[g_AccountName]) do
	
		--IV_Log( "Processing Realm: " .. Realm );
		if ( Realm == "Internal" ) then
			g_InventoryViewerTable[g_AccountName][Realm] = nil;
		
		elseif ( Realm and Realm ~= INTERNAL_SETTINGS_TABLE ) then

			for CharName,junk2 in pairs(g_InventoryViewerTable[g_AccountName][Realm]) do

				--IV_Log( "Processing CharName: " .. CharName );
				
				if ( CharName and CharName ~= "Chest" ) then
				
					-- Clear this characters chest
					if ( g_InventoryViewerTable[g_AccountName][Realm][CharName].Chest ) then
						g_InventoryViewerTable[g_AccountName][Realm][CharName].Chest = nil;
						
						--IV_Log( "deleted " .. g_AccountName .. "/" .. Realm .. "/" .. CharName .. "'s Chest." );
					end
					
				elseif ( CharName and CharName == "Chest" ) then

					-- Delete the global Chest
					g_InventoryViewerTable[g_AccountName][Realm].Chest = nil;
					
					--IV_Log( "deleted " .. g_AccountName .. "/" .. Realm .. "'s Chest." );
				end
			end
		end
	end
	
	IV_Log( "Please re-visit all of your House Chests as Inventory Viewer had to remove the old incompatible data from its database.  Sorry for the inconvenience.", 0.7,0,0 );
end

function IV_Remove07HangerTables()

	--IV_Log( "begin IV_Remove07HangerTables" );
	
	-- Cycle through realms we have saved in old data files
	for Realm,junk in pairs(g_InventoryViewerTable[g_AccountName]) do
	
		--IV_Log( "Processing Realm: " .. Realm );
		if ( Realm == "Internal" ) then
			g_InventoryViewerTable[g_AccountName][Realm] = nil;
		end
		
		if ( Realm and Realm ~= INTERNAL_SETTINGS_TABLE ) then

			for CharName,junk2 in pairs(g_InventoryViewerTable[g_AccountName][Realm]) do

				--IV_Log( "Processing CharName: " .. CharName );
				
				if ( CharName and CharName ~= "Chest" ) then
				
					-- Clear placeholders in Bank (all 40 slots)
					if ( g_InventoryViewerTable[g_AccountName][Realm][CharName].Hanger ) then
						g_InventoryViewerTable[g_AccountName][Realm][CharName].Hanger = nil;
						
						--IV_Log( "deleted " .. g_AccountName .. "/" .. Realm .. "/" .. CharName .. ".Hanger table." );
					end
				end
			end
		end
	end
	
	--IV_Log( "end IV_Remove07HangerTables" );
end


function IV_MoveDataFromFileToSavedVariables()

	tblAccount = {};
	
	-- Load old Data
	table.copy(tblAccount,table.load(GC_GetGlobalPath(TABLE_DATA_FILENAME)));
	
	
	-- Clear out any empty placeholders as they are a waste of space and we don't need them anymore
	
	-- Cycle through realms we have saved in old data files
	for Realm,junk in pairs(tblAccount) do
	
		--IV_Log( "Processing Realm: " .. Realm );
		if ( Realm == "Internal" ) then
			g_InventoryViewerTable[g_AccountName][Realm] = nil;
		end
		
		if ( Realm and Realm ~= INTERNAL_SETTINGS_TABLE ) then

			for CharName,junk2 in pairs(tblAccount[Realm]) do

				--IV_Log( "Processing CharName: " .. CharName );
				
				if ( CharName and CharName ~= "Chest" ) then
				
					-- Clear placeholders in House Chest (all 20 slots)
					if ( tblAccount[Realm][CharName].Chest ) then
					
						for i = 1, 20 do
							if ( tblAccount[Realm][CharName].Chest[i] and
								 ( not tblAccount[Realm][CharName].Chest[i].itemLink or 
								   tblAccount[Realm][CharName].Chest[i].itemLink == "" ) ) then
								 
								 -- wipe out this chest item
								 tblAccount[Realm][CharName].Chest[i] = nil;
								 
							elseif ( tblAccount[Realm][CharName].Chest[i] and
									 tblAccount[Realm][CharName].Chest[i].slot ) then
								
								-- wipe out the slot as it was never needed
								tblAccount[Realm][CharName].Chest[i].slot = nil;
							end
						end
					end
					
					-- Clear placeholders in Bank (all 40 slots)
					if ( tblAccount[Realm][CharName].Bank ) then
						for i = 1, 40 do
							if ( tblAccount[Realm][CharName].Bank[i] and
								 ( not tblAccount[Realm][CharName].Bank[i].itemLink or 
								   tblAccount[Realm][CharName].Bank[i].itemLink == "" ) ) then
								 
								 -- wipe out this bank item
								 tblAccount[Realm][CharName].Bank[i] = nil;
								 
							elseif ( tblAccount[Realm][CharName].Bank[i] and
									 tblAccount[Realm][CharName].Bank[i].slot ) then
								
								-- wipe out the slot as it was never needed
								tblAccount[Realm][CharName].Bank[i].slot = nil;
							end
						end
					end
					
					-- Clear placeholders in Backpack (all 60 slots)
					if ( tblAccount[Realm][CharName].Backpack ) then
						for i = 1, 60 do
							if ( tblAccount[Realm][CharName].Backpack[i] and
								 ( not tblAccount[Realm][CharName].Backpack[i].itemLink or 
								   tblAccount[Realm][CharName].Backpack[i].itemLink == "" ) ) then
								 
								 -- wipe out this backpack item
								 tblAccount[Realm][CharName].Backpack[i] = nil;
								 
							elseif ( tblAccount[Realm][CharName].Backpack[i] and
									 tblAccount[Realm][CharName].Backpack[i].slot ) then
								
								-- wipe out the slot as it was never needed
								tblAccount[Realm][CharName].Backpack[i].slot = nil;
							end
						end
					end
				end
			end
		end
	end
	
	-- insert into savable table
	g_InventoryViewerTable[g_AccountName] = tblAccount;
	
			
	-- Save it to the SaveVariables.lua file (bloat bloat bloat)
	SaveVariables("g_InventoryViewerTable");

	--IV_Log( "End IV_MoveDataFromFileToSavedVariables" );
end

function IVFrame_LoadTable()

	--IV_Log("IVFrame_LoadTable - loading " .. TABLE_DATA_FILENAME);
	--table.copy(g_InventoryViewerTable[g_AccountName],table.load(GC_GetGlobalPath(TABLE_DATA_FILENAME)));	
	--IV_Log("Loaded " .. TABLE_DATA_FILENAME );
end


function IVFrame_SaveTable()

	--IV_Log("IVFrame_SaveTable - saving " .. TABLE_DATA_FILENAME);
	SaveVariables("g_InventoryViewerTable");
	--table.save(g_InventoryViewerTable[g_AccountName],GC_GetGlobalPath(TABLE_DATA_FILENAME));
	--IV_Log("Saved ".. TABLE_DATA_FILENAME);
end


function IVFrame_SetTable_WeaponRack( DBID )

	--IV_Log( "IVFrame_SetTable_WeaponRack" );
	
	if ( IV_IsInitialized() == false ) then
		IV_Log("Error: InventoryViewer not initialized yet.");
		return;
	end
	
	if ( g_CurrentPlayerName == nil or g_CurrentPlayerName == "" ) then
		g_CurrentPlayerName = UnitName("player");
	end

	if ( HouseHangerFrame1:IsVisible() ) then
		--IV_Log( "HouseHangerFrame1:IsVisible()" );
	end
	if ( HouseHangerFrame2:IsVisible() ) then
		--IV_Log( "HouseHangerFrame2:IsVisible()" );
	end

	-- Weapon Racks only hold 2 items, MainHand (ID:16)  and OffHand (ID:17)
	
	-- Main Hand
	local itemLinkMainHand = Houses_GetItemLink( DBID, 16 );		-- 16 is Main Hand
	local textureMainHand = Houses_GetItemInfo( DBID, 16 );

	if ( itemLinkMainHand and textureMainHand ) then

		local tempObj = {};
		tempObj.texture = textureMainHand;
		tempObj.itemLink = itemLinkMainHand;
		
		if ( not g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack ) then
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack = {};
		end
		
		g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack[1] = {};
		g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack[1] = tempObj;
	else
		if (  g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack and
			  g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack[1] ) then
			  
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack[1] = nil;
		end
	end
	
	-- OffHand 
	local itemLinkOffHand = Houses_GetItemLink( DBID, 17 );	-- 17 is Secondary Hand (OffHand)
	local textureOffHand = Houses_GetItemInfo( DBID, 17 );

	if ( itemLinkOffHand and textureOffHand ) then

		local tempObj = {};
		tempObj.texture = textureOffHand;
		tempObj.itemLink = itemLinkOffHand;
		
		if ( not g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack ) then
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack = {};
		end

		g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack[2] = {};
		g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack[2] = tempObj;
	else
		if (  g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack and
			  g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack[2] ) then
			  
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].WeaponRack[2] = nil;
		end
	end
end


function IVFrame_SetTable_ClothesRack( DBID )
	
	--IV_Log( "IVFrame_SetTable_ClothesRack" );
	
	if ( IV_IsInitialized() == false ) then
		IV_Log("Error: InventoryViewer not initialized yet.");
		return;
	end
	
	if ( g_CurrentPlayerName == nil or g_CurrentPlayerName == "" ) then
		g_CurrentPlayerName = UnitName("player");
	end

	if ( HouseHangerFrame1:IsVisible() ) then
		--IV_Log( "HouseHangerFrame1:IsVisible()" );
	end
	if ( HouseHangerFrame2:IsVisible() ) then
		--IV_Log( "HouseHangerFrame2:IsVisible()" );
	end
	
	-- House Hanger (Clothes Rack) is displayed in a specific (messed up) order, store it in their position so we'll display it the same way
	-- 1 8 (head, shoulder)
	-- 4 2 (chest, gloves)
	-- 7 3 (belt, boots)
	-- 5 6 (pants, cape)
	-- 9 10 (Main/Offhand)
	
	-- Out database will store them as (MySlot = OriginalHangerId):
	-- 1=1 (head)		2=8 (shoulder)
	-- 3=4 (chest)	4=2 (gloves)
	-- 5=7 (belt)		6=3 (boots)
	-- 7=5 (pants)	8=6 (cape)
	-- 9=9 (mainhand)	10=10 (offhand)

	-- Loop through the 8 (10) available slots on the Inventory Viewer UI (last 2 are weapons, ignore for now as they are disabled)
	for i=1,8 do
	
		-- re-arrange numbers to make up for House Hanger order
		local idxHangerId = i;
		
		-- 1, 9, and 10 stay the same, re-arrange the others before requesting the item info
		if ( i == 2 ) then idxHangerId = 8; end;
		if ( i == 3 ) then idxHangerId = 4; end;
		if ( i == 4 ) then idxHangerId = 2; end;
		if ( i == 5 ) then idxHangerId = 7; end;
		if ( i == 6 ) then idxHangerId = 3; end;
		if ( i == 7 ) then idxHangerId = 5; end;
		if ( i == 8 ) then idxHangerId = 6; end;
		
		local itemLink = Houses_GetItemLink( DBID, idxHangerId );
		local texture = Houses_GetItemInfo( DBID, idxHangerId );

		if ( itemLink and texture ) then
			--IV_Log( i .. ": " .. itemLink .. "(" .. texture .. ")" );
			local tempObj = {};
			tempObj.texture = texture;
			tempObj.itemLink = itemLink;
			
			if ( not g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].ClothesRack ) then
				g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].ClothesRack = {};
			end
			
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].ClothesRack[i] = {};
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].ClothesRack[i] = tempObj;
		else
			if (  g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].ClothesRack and
				  g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].ClothesRack[i] ) then
				  
				g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].ClothesRack[i] = nil;
			end
		end
	end
end


function IVFrame_SetTable_HouseChest( DBID, HouseItemName )

	--IV_Log( "IVFrame_SetTable_HouseChest " .. DBID ..": " .. HouseItemName );

	if ( IV_IsInitialized() == false ) then
		IV_Log("Error: InventoryViewer not initialized yet.");
		return;
	end
	
	if ( g_CurrentPlayerName == nil or g_CurrentPlayerName == "" ) then
		g_CurrentPlayerName = UnitName("player");
	end

	if ( not g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Chest[DBID] ) then
		g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Chest[DBID] = {};
		g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Chest[DBID].Name = HouseItemName;
	end

	-- If a default display house chest isn't set yet, set it to this one
	if ( not g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].SelectedChestDBID ) then
		g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].SelectedChestDBID = DBID;
		--IV_Log( "IVFrame_SetTable_HouseChest - Set default display House Chest to DBID: " .. DBID .. " (" .. HouseItemName .. ")" );
	end

	-- Set  Chest info (all 20 slots)
	for i = 1, 20 do

		local itemLink = "";
		local texture, name, itemCount, locked = Houses_GetItemInfo( DBID, i );

		if ( texture ) then

			-- we can only attempt to get an itemLink if it's not an empty slot
			itemLink = Houses_GetItemLink( DBID, i );

			local tempObj = {};
			tempObj.texture = texture;
			tempObj.itemCount = itemCount;
			tempObj.itemLink = itemLink;
			
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Chest[DBID][i] = {};
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Chest[DBID][i] = tempObj;
		else
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Chest[DBID][i] = nil;
		end
	end
	
	--IV_Log("Stored House Chest " .. DBID);
end


function IVFrame_SetTable_Bank()

	--IV_Log( "IVFrame_SetTable_Bank" );
	
	if ( IV_IsInitialized() == false ) then
		IV_Log("Error: InventoryViewer not initialized yet.");
		return;
	end
	
	if ( g_CurrentPlayerName == nil or g_CurrentPlayerName == "" ) then
		g_CurrentPlayerName = UnitName("player");
	end
	
	-- Set  Bank info (all 200 slots)
	for i = 1, 200 do

		local itemLink = "";
		local texture, name, itemCount, locked = GetBankItemInfo(i);
		
		if ( texture ) then

			-- we can only attempt to get an itemLink if it's not an empty slot
			itemLink = GetBankItemLink(i);
			
			local tempObj = {};
			tempObj.texture = texture;
			tempObj.itemCount = itemCount;
			tempObj.itemLink = itemLink;
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Bank[i] = {};
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Bank[i] = tempObj;
		else
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Bank[i] = nil;
		end
	end

	--IV_Log("Stored Bank.");
end


function IVFrame_SetTable_Backpack()

	--IV_Log( "IVFrame_SetTable_Backpack" );
	
	if ( IV_IsInitialized() == false ) then
		IV_Log("Error: InventoryViewer not initialized yet.");
		return;
	end
	
	if ( g_CurrentPlayerName == nil or g_CurrentPlayerName == "" ) then
		g_CurrentPlayerName = UnitName("player");
	end
	
	-- Set  BackPack info (all 180 slots, 30 per page)
	for i = 1, 180 do
	
		local itemLink = "";
		local index, texture, name, itemCount, locked, invalid = GetBagItemInfo(i);
		
		if ( texture ~= "" ) then

			-- we can only attempt to get an itemLink if it's not an empty slot
			itemLink = GetBagItemLink( index );
			
			local tempObj = {};
			tempObj.texture = texture;
			tempObj.itemCount = itemCount;
			tempObj.itemLink = itemLink;
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Backpack[i] = {};
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Backpack[i] = tempObj;
		else
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].Backpack[i] = nil;
		end
	end
	
	--IV_Log("Stored Backpack.");
end


function IVFrame_SetTable_ItemShopBag()

	--IV_Log( "IVFrame_SetTable_ItemShopBag" );
	
	if ( IV_IsInitialized() == false ) then
		IV_Log("Error: InventoryViewer not initialized yet.");
		return;
	end
	
	if ( g_CurrentPlayerName == nil or g_CurrentPlayerName == "" ) then
		g_CurrentPlayerName = UnitName("player");
	end
	
	-- Set  ItemShop Bag info (all 50 slots)
	for i = 1, 50 do
	
		local itemLink = "";
		local texture, name, itemCount, locked = GetGoodsItemInfo(i);
		
		if ( texture ~= "" ) then

			-- we can only attempt to get an itemLink if it's not an empty slot
			itemLink = GetBagItemLink(i);
			
			local tempObj = {};
			tempObj.texture = texture;
			tempObj.itemCount = itemCount;
			tempObj.itemLink = itemLink;
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].ItemShopBag[i] = {};
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].ItemShopBag[i] = tempObj;
		else
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].ItemShopBag[i] = nil;
		end
	end
	
	--IV_Log("Stored Item Shop Bag.");
end


function IVFrame_ClearTable()

	--IV_Log("IVFrame_ClearTable");
	g_InventoryViewerTable[g_AccountName] = {};
	IVFrame_SaveTable();
end


function IVItemButton_OnLeave(this)

	--IV_Log( "IVItemButton_OnLeave" )
	GameTooltip:Hide();
end


function IVItemButton_GetItemLinkFromTable( ID )

	local itemLink = "";

	-- bank item
	if ( ID > 0 and ID < 41 ) then

		-- The Table supports all 200 slots, figure out which slot in the Bank table we're on
		local TableIndex = ( IVFrameBankBackdropFrame.PageIndex - 1 ) * 40 + ID;
		
		if (g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].Bank[TableIndex] and 
			g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].Bank[TableIndex].itemLink) then
			
			itemLink = g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].Bank[TableIndex].itemLink;
		end
	
	-- backpack item
	elseif ( ID > 40 and ID < 101 ) then
	
		-- The Table supports all 180 slots, figure out which slot in the Backpack table we're on
		local TableIndex = ( IVFrameBackpackBackdropFrame.PageIndex - 1 ) * 60 + (ID-40);
	
		if (g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].Backpack[TableIndex] and
			g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].Backpack[TableIndex].itemLink) then
			
			itemLink = g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].Backpack[TableIndex].itemLink;
		end

	-- house chest  item
	elseif ( ID > 100 and ID < 121 ) then
	
		local selected = IV_GetSelectedChest( g_IVSelectedCharTabName );
	
		-- bail if we couldn't get or set a chest as selected, this user doesn't have any chests.
		if ( selected ) then
		
			if (g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].Chest and
				g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].Chest[selected] and 
				g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].Chest[selected][ID-100] and 
				g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].Chest[selected][ID-100].itemLink) then
				
				itemLink = g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].Chest[selected][ID-100].itemLink;
			end
		end
		
	-- house hanger - Clothes Rack
	elseif ( ID > 120 and ID < 129 ) then
	
		if (g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].ClothesRack and
			g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].ClothesRack[ID-120] and
			g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].ClothesRack[ID-120].itemLink) then
			
			itemLink = g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].ClothesRack[ID-120].itemLink;
		end
		
	-- house hanger - WeaponRack
	elseif ( ID > 128 and ID < 131 ) then
	
		if (g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].WeaponRack and
			g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].WeaponRack[ID-128] and
			g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].WeaponRack[ID-128].itemLink) then
			
			itemLink = g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].WeaponRack[ID-128].itemLink;
		end
	end
	
	return itemLink;
end


function IV_GetItemID( itemLink )

	local ret = "";
	local _type, _data, _name = ParseHyperlink(itemLink);
	
	if ( _data and _data ~= "" ) then
		_,_,ret = string.find(_data, "(%x+)");
	end
	
	return ret;
end


function IV_GetListOfItems( itemLink )

	local text = "";
	local total = 0;
	
	-- Loop through all items in the global chest

	-- Cycle thru all the characters on this server
	
	for CharName, CharTable in pairs( g_InventoryViewerTable[g_AccountName][g_Realm] ) do

		if ( CharName and  CharName ~= "Chest" ) then
		
			-- Loop through all Chests for CharName
			--IV_Log( "Searching " .. CharName .. "'s Chests for " .. itemLink );
			
			if ( CharTable.Chest ) then
				
				for DBID, ChestTable in pairs( CharTable.Chest ) do
				
					local tmpChest = 0;
					
					-- Loop through each item in the current chest searching for the specified itemLink
					for i=1, 20 do
					
						if (ChestTable[i] and ChestTable[i].itemLink) then

							if ( IV_GetItemID(itemLink) == IV_GetItemID(ChestTable[i].itemLink) ) then
								tmpChest = tmpChest + ChestTable[i].itemCount or 0;
								--IV_Log( "tmpChest: " .. tmpChest );
							end
						end
					end
					
					-- If we found any, set the  text
					if ( tmpChest > 0 ) then
						text = text .. tmpChest .. " in " .. CharName .. APOSTRAPHE_S .. " " .. ChestTable.Name .. "\n";
						total = total + tmpChest;
					end
				end			
			end
			
			-- Loop through all bank items for CharName
			--IV_Log( "Searching " .. CharName .. "'s Bank for " .. itemLink );
			
			local tmpBank = 0;
			
			for i=1, 200 do
			
				if (g_InventoryViewerTable[g_AccountName][g_Realm][CharName].Bank and 
					g_InventoryViewerTable[g_AccountName][g_Realm][CharName].Bank[i] and 
					g_InventoryViewerTable[g_AccountName][g_Realm][CharName].Bank[i].itemLink) then

					if ( IV_GetItemID(itemLink) == IV_GetItemID(g_InventoryViewerTable[g_AccountName][g_Realm][CharName].Bank[i].itemLink) ) then
						tmpBank = tmpBank + g_InventoryViewerTable[g_AccountName][g_Realm][CharName].Bank[i].itemCount or 0;
						--IV_Log( "tmpBank: [" .. i .. "] " .. tmpBank );
					end
					
				else
					--IV_Log( "Slot " .. i .. " is empty." );
				end
			end
			
			-- If we found any, set the  text
			if ( tmpBank > 0 ) then
				text = text .. tmpBank .. " in " .. CharName .. APOSTRAPHE_S .. " " .. TEXT_BANK .. "\n";
				total = total + tmpBank;
			end
			
			
			-- Loop through all the backpack items for CharName
			
			--IV_Log( "Searching " .. CharName .. "'s Backpack for " .. itemLink );
			local tmpBackpack = 0;
			for i=1, 180 do
			
				if (g_InventoryViewerTable[g_AccountName][g_Realm][CharName].Backpack and 
					g_InventoryViewerTable[g_AccountName][g_Realm][CharName].Backpack[i] and 
					g_InventoryViewerTable[g_AccountName][g_Realm][CharName].Backpack[i].itemLink) then

					if ( IV_GetItemID(itemLink) == IV_GetItemID(g_InventoryViewerTable[g_AccountName][g_Realm][CharName].Backpack[i].itemLink) ) then
						tmpBackpack = tmpBackpack + g_InventoryViewerTable[g_AccountName][g_Realm][CharName].Backpack[i].itemCount or 0;
						--IV_Log( "tmpBackpack: [" .. i .. "] " .. tmpBackpack );
					end
				end
			end
			
			-- If we found any, set the  text
			if ( tmpBackpack > 0 ) then
				text = text .. tmpBackpack .. " in " .. CharName .. APOSTRAPHE_S .. " " .. TEXT_BACKPACK .. "\n";
				total = total + tmpBackpack;
			end
			
			
			-- Loop through all the item shop backpack items for CharName
			
			--IV_Log( "Searching " .. CharName .. "'s Item Shop Backpack for " .. itemLink );
			local tmpItemShopBag = 0;
			for i=1, 50 do
			
				if (g_InventoryViewerTable[g_AccountName][g_Realm][CharName].ItemShopBag and 
					g_InventoryViewerTable[g_AccountName][g_Realm][CharName].ItemShopBag[i] and 
					g_InventoryViewerTable[g_AccountName][g_Realm][CharName].ItemShopBag[i].itemLink) then

					if ( IV_GetItemID(itemLink) == IV_GetItemID(g_InventoryViewerTable[g_AccountName][g_Realm][CharName].ItemShopBag[i].itemLink) ) then
						tmpItemShopBag = tmpItemShopBag + g_InventoryViewerTable[g_AccountName][g_Realm][CharName].ItemShopBag[i].itemCount or 0;
						--IV_Log( "tmpItemShopBag: [" .. i .. "] " .. tmpItemShopBag );
					end
				end
			end
			
			-- If we found any, set the  text
			if ( tmpItemShopBag > 0 ) then
				text = text .. tmpItemShopBag .. " in " .. CharName .. APOSTRAPHE_S .. " " .. TEXT_ITEMSHOP_BACKPACK .. "\n";
				total = total + tmpItemShopBag;
			end
			
			
			-- Loop through all the clothes rack items for CharName
			--IV_Log( "Searching " .. CharName .. "'s Clothes Rack for " .. itemLink );
			local tmpClothesRack = 0;
			for i=1, 8 do
			
				if (g_InventoryViewerTable[g_AccountName][g_Realm][CharName].ClothesRack and 
					g_InventoryViewerTable[g_AccountName][g_Realm][CharName].ClothesRack[i] and 
					g_InventoryViewerTable[g_AccountName][g_Realm][CharName].ClothesRack[i].itemLink) then

					if ( IV_GetItemID(itemLink) == IV_GetItemID(g_InventoryViewerTable[g_AccountName][g_Realm][CharName].ClothesRack[i].itemLink) ) then
						tmpClothesRack = tmpClothesRack + 1;
						--IV_Log( "tmpClothesRack: [" .. i .. "] " .. tmpClothesRack );
					end
				end
			end
				
			-- If we found any, set the  text
			if ( tmpClothesRack > 0 ) then
				text = text .. tmpClothesRack .. " on " .. CharName .. APOSTRAPHE_S .. " " .. TEXT_CLOTHES_RACK .. "\n";
				total = total + tmpClothesRack;
			end


			-- Loop through all the weapon rack items for CharName
			--IV_Log( "Searching " .. CharName .. "'s Weapon Rack for " .. itemLink );
			local tmpWeaponRack = 0;
			for i=1, 2 do
			
				if (g_InventoryViewerTable[g_AccountName][g_Realm][CharName].WeaponRack and 
					g_InventoryViewerTable[g_AccountName][g_Realm][CharName].WeaponRack[i] and 
					g_InventoryViewerTable[g_AccountName][g_Realm][CharName].WeaponRack[i].itemLink) then

					if ( IV_GetItemID(itemLink) == IV_GetItemID(g_InventoryViewerTable[g_AccountName][g_Realm][CharName].WeaponRack[i].itemLink) ) then
						tmpWeaponRack = tmpWeaponRack + 1;
						--IV_Log( "tmpWeaponRack: [" .. i .. "] " .. tmpWeaponRack );
					end
				end
			end
				
			-- If we found any, set the  text
			if ( tmpWeaponRack > 0 ) then
				text = text .. tmpWeaponRack .. " on " .. CharName .. APOSTRAPHE_S .. " " .. TEXT_WEAPON_RACK .. "\n";
				total = total + tmpWeaponRack;
			end
		end
	end
	
	if ( text ~= "" and total > 0 ) then
		text = text .. "|cffFCCF00" .. total .. " total|r\n";
	end
	
	--IV_Log( "Returning found item text: " .. text );
	return text;
end


function IVItemButton_OnEnter(this)

	--IV_Log( "IVItemButton_OnEnter" );
	
	local itemLink = IVItemButton_GetItemLinkFromTable( this:GetID() );

	if ( itemLink ~= "" ) then
	
		-- Set the Tooltip to the itemLink info
		GameTooltip:ClearAllAnchors();
		GameTooltip:SetAnchor("TOPLEFT", "BOTTOMRIGHT", this:GetName(),2,0);
		
		-- Show the tooltip
		GameTooltip:Show();
		GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT", 0, 0);
		GameTooltip:SetHyperLink( itemLink );
	end
end


function IVItemButton_OnClick(this, button, ignoreShift)

	--IV_Log( "IVItemButton_OnClick" )
	
	local itemLink = IVItemButton_GetItemLinkFromTable( this:GetID() );

	if ( button == "LBUTTON" ) then
		if( IsShiftKeyDown() ) then
			--IV_Log( "Shift key is down" .. this:GetID() );
			if( ITEMLINK_EDITBOX )then
				ChatEdit_AddItemLink( itemLink );
				return;
			end
		elseif( IsCtrlKeyDown() ) then
			--IV_Log( "Ctrl key is down" )
			ItemPreviewFrame_SetItemLink( ItemPreviewFrame, itemLink );
			return;
		end
	end
end


function IVFrame_OnLoad(this)	

	--IV_Log( "IVFrame_OnLoad" );

	UIPanelBackdropFrame_SetTexture( this, "Interface/Common/PanelCommonFrame", 256 , 256 );


	-- Set anchors and Bank button locations, anchored off IVItemButton1
	local itemButton;
	local nBankStartingSlot = 1;
	local nBankEndingSlot = 40;
	local nBankSlotsPerLine = 5;
	local nBankEndOfTopLineSlotNumber = nBankStartingSlot + nBankSlotsPerLine - 1;

	local BankText = getglobal("IVFrameBackdropBank");
	if ( BankText ) then
		BankText:SetText( TEXT_BANK .. " :" );
--		BankText:ClearAllAnchors();
--		BankText:SetAnchor("TOPLEFT", "TOPLEFT", "IVItemButton1", 0, -15);
	end
	
	-- skip first slot as it's Anchor is set in the XML file
	for i = nBankStartingSlot+1, nBankEndingSlot do
		itemButton = getglobal("IVItemButton" .. i);
		if ( itemButton ) then
			itemButton:ClearAllAnchors();
			if (i <= nBankEndOfTopLineSlotNumber) then
				itemButton:SetAnchor("TOPLEFT", "TOPRIGHT", "IVItemButton" .. (i-1), 4, 0);
			else				
				itemButton:SetAnchor("TOPLEFT", "BOTTOMLEFT", "IVItemButton" .. (i-nBankSlotsPerLine), 0, 4);
			end
		end
	end
	
	-- Set anchors and Backpack button locations, anchored off IVItemButton41
	local nBackPackStartingSlot = 41;
	local nBackpackEndingSlot = 100;
	local nBackpackSlotsPerLine = 6;
	local nBackpackEndOfTopLineSlotNumber = nBackPackStartingSlot + nBackpackSlotsPerLine - 1;
	
	local BackPackText = getglobal("IVFrameBackdropBackpack");
	if ( BackPackText ) then
		BackPackText:SetText( TEXT_BACKPACK .. " :" );
--		BackPackText:ClearAllAnchors();
--		BackPackText:SetAnchor("TOPLEFT", "TOPLEFT", "IVItemButton41", 0, -15);
	end

	-- skip first slot as it's Anchor is set in the XML file
	for i = nBackPackStartingSlot+1, nBackpackEndingSlot do
		itemButton = getglobal("IVItemButton" .. i);
		if ( itemButton ) then
			itemButton:ClearAllAnchors();
			if (i <= nBackpackEndOfTopLineSlotNumber) then
				itemButton:SetAnchor("TOPLEFT", "TOPRIGHT", "IVItemButton" .. (i-1), 4, 0);
			else				
				itemButton:SetAnchor("TOPLEFT", "BOTTOMLEFT", "IVItemButton" .. (i-nBackpackSlotsPerLine), 0, 4);
			end
		end
	end

	
	-- Set anchors and House Chest button locations, anchored off IVItemButton101
	local nHouseChestStartingSlot = 101;
	local nHouseChestEndingSlot = 120;
	local nHouseChestSlotsPerLine = 5;
	local nHouseChestEndOfTopLineSlotNumber = nHouseChestStartingSlot + nHouseChestSlotsPerLine - 1;
	
	local HouseChestText = getglobal("IVFrameBackdropHouseChest");
	if ( HouseChestText ) then
		HouseChestText:SetText( TEXT_HOUSE_CHEST );
--		HouseChestText:ClearAllAnchors();
--		HouseChestText:SetAnchor("TOP", "TOP", "IVItemButton103", 0, -15);
	end

	-- skip first slot as it's Anchor is set in the XML file
	for i = nHouseChestStartingSlot+1, nHouseChestEndingSlot do
		itemButton = getglobal("IVItemButton" .. i);
		if ( itemButton ) then
			itemButton:ClearAllAnchors();
			if (i <= nHouseChestEndOfTopLineSlotNumber) then
				itemButton:SetAnchor("TOPLEFT", "TOPRIGHT", "IVItemButton" .. (i-1), 4, 0);
			else				
				itemButton:SetAnchor("TOPLEFT", "BOTTOMLEFT", "IVItemButton" .. (i-nHouseChestSlotsPerLine), 0, 4);
			end
		end
	end

	
	-- Set anchors and House Hanger button locations, anchored off IVItemButton121
	local nHouseHangerStartingSlot = 121;
	local nHouseHangerEndingSlot = 130;
	local nHouseHangerSlotsPerLine = 2;
	local nHouseHangerEndOfTopLineSlotNumber = nHouseHangerStartingSlot + nHouseHangerSlotsPerLine - 1;
	
	local HouseHangerText = getglobal("IVFrameBackdropHouseHanger");
	if ( HouseHangerText ) then
		HouseHangerText:SetText( TEXT_CLOTHES_WEAPON_RACK );
		HouseHangerText:ClearAllAnchors();
		HouseHangerText:SetAnchor("TOPLEFT", "TOPLEFT", "IVItemButton121", 0, -20);
	end

	-- skip first slot as it's Anchor is set in the XML file
	for i = nHouseHangerStartingSlot+1, nHouseHangerEndingSlot do
		itemButton = getglobal("IVItemButton" .. i);
		if ( itemButton ) then
			itemButton:ClearAllAnchors();
			
			if (i <= nHouseHangerEndOfTopLineSlotNumber) then
				itemButton:SetAnchor("TOPLEFT", "TOPRIGHT", "IVItemButton" .. (i-1), 4, 0);
			else				
				itemButton:SetAnchor("TOPLEFT", "BOTTOMLEFT", "IVItemButton" .. (i-nHouseHangerSlotsPerLine), 0, 4);
			end
		end
	end
	
		
	-- register for events
	this:RegisterEvent("VARIABLES_LOADED");
	this:RegisterEvent("UNIT_PORTRAIT_UPDATE");			-- Used to initialize this AddOn (it gets hit a few times, and on one of them we'll be able to get the player name
	this:RegisterEvent("PLAYER_BAG_CHANGED");			-- item in bag was used, added, removed, moved (this seems to be called twice sometimes), 
	this:RegisterEvent("HOUSES_STORAGE_SHOW" );			-- 1 when opening chest, 1 when adding or removing item from chest, 2 when moving an item between slots within the chest
	this:RegisterEvent("BANK_UPDATE");					-- This is called when you rent more bank space
	this:RegisterEvent("HOUSES_HANGER_SHOW");
	this:RegisterEvent("HOUSES_HANGER_CHANGED");

	getglobal( this:GetName() .. "Title" ):SetText( PLUGIN_TITLE );
	
	--IV_Log( "end IVFrame_OnLoad" );
end


function IVFrame_OnShow(this)

	--IV_Log("IVFrame_OnShow");
	this:ResetFrameOrder();
end


function IVFrame_OnHide(this)
	--IV_Log("IVFrame_OnHide");
	this:Hide();
end


function IsStorageChest( HouseItemName )

	--IV_Log("IsStorageChest: " .. HouseItemName);

	local bRet = false;
	local SearchTable = {};
	local UpperHouseItemName = HouseItemName:upper();
	
	if ( GetLocation() == "DE" ) then
		SearchTable = CHEST_SEARCH_TABLE_DE;
	else
		SearchTable = CHEST_SEARCH_TABLE;
	end
	
	for i,SearchFor in pairs(SearchTable) do
	
		--IV_Log( SearchFor );
		
		if ( string.find( UpperHouseItemName, SearchFor:upper() ) ) then
		
			--IV_Log( "*Found " .. SearchFor .. " in " .. HouseItemName .. ", this is a Chest!" );
			bRet = true;
			
			-- We found a hit, we're done here
			break;
		end
	end
	
	return bRet;
end

function IVFrame_OnEvent(this, event, arg1, arg2)

	--IV_Log("IVFrame_OnEvent Event: "..event);
	--if ( arg1 ) then IV_Log("arg1: " .. tostring(arg1)); end
	--if ( arg2 ) then IV_Log("arg2: " .. tostring(arg2)); end
	


	-- Event: VARIABLES_LOADED
	-- we load our .INI file here
	if ( event == "VARIABLES_LOADED" ) then
		--IVFrame_LoadTable();

        -- If the user does a /script ReloadUI() only this event will be called.
		if ( IV_IsInitialized() ~= true ) then
			IV_Initialize();
		end

		return;
	end


	-- Event: UNIT_PORTRAIT_UPDATE
	-- We Get the player name here and save off initial Backpack info
	if ( event == "UNIT_PORTRAIT_UPDATE" and arg1 == "player" ) then
		
		if ( IV_IsInitialized() ~= true ) then
			IV_Initialize();
		end

		return;
	end

	
	-- Event: PLAYER_BAG_CHANGED
	-- When you move an item around in your backpack you get one of these.  Taking a potion also causes 2 hits on this (might be because I have 2 pages with pots using same cooldown)
	if ( event == "PLAYER_BAG_CHANGED" ) then
		
		IVFrame_SetTable_Backpack();
		IVFrame_SetTable_ItemShopBag();
		IVFrame_SetTable_Bank();

		if ( IV_IsSelectedTabCurrentPlayer() ) then
			PopulateBackpackInventory( g_CurrentPlayerName );
			PopulateBankInventory( g_CurrentPlayerName );
		end

		-- save latest changes to our global object
		IVFrame_SaveTable();
		return;
	end

	
	-- Event: HOUSES_HANGER_SHOW
	-- Event: HOUSES_HANGER_CHANGED
	-- User opened the House Hanger or change clothes on hanger
	if ( event == "HOUSES_HANGER_SHOW" or event == "HOUSES_HANGER_CHANGED" ) then
	
		local ID , DBID = Houses_GetFocusFurnishingID();
		local _,HouseItemName = Houses_GetItemInfo( DBID, -1 );

		--IV_Log( "ID/DBID: " .. ID .. "/" .. DBID .. " Name: " .. HouseItemName );
		
		if ( string.find( HouseItemName, TEXT_CLOTHES_RACK ) ) then
		
			IVFrame_SetTable_ClothesRack( DBID );	
			
		elseif ( string.find( HouseItemName, TEXT_WEAPON_RACK ) ) then
		
			IVFrame_SetTable_WeaponRack( DBID );
		end

		
		if ( IV_IsSelectedTabCurrentPlayer() ) then
			PopulateHouseHangerInventory( g_CurrentPlayerName );
		end
		
		return;
	end
	
	
	-- Event: HOUSES_STORAGE_SHOW
	-- When you move an item around in your house chest this event will be called
	if ( event == "HOUSES_STORAGE_SHOW" ) then

		local ID , DBID = Houses_GetFocusFurnishingID();
		local _,HouseItemName = Houses_GetItemInfo( DBID, -1 );
		
		--IV_Log( "ID/DBID: " .. ID .. "/" .. DBID .. " Name: " .. HouseItemName );
		
		if ( IsStorageChest( HouseItemName ) ) then
			IVFrame_SetTable_HouseChest( DBID, HouseItemName );
		end
		
		-- If a default display house chest isn't set yet, set it to this one
		if ( not g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].SelectedChestDBID ) then
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CurrentPlayerName].SelectedChestDBID = DBID;
		end
		
		local selected = IV_GetSelectedChest( g_CurrentPlayerName );

		-- Update the UI only if the currently selected tab is the logged in user and the Chest is set to the Chest this user just opened/updated
		if ( IV_IsSelectedTabCurrentPlayer() and selected and selected == DBID ) then
			PopulateHouseChestInventory( g_CurrentPlayerName );
		end

		-- save latest changes to our global object
		IVFrame_SaveTable();
		return;
	end

	
	-- Event: BANK_UPDATE
	-- This BANK_UPDATE is called when a player rents more bank space (it may also be called when that rental excpires, not sure yet)
	if ( event == "BANK_UPDATE" ) then
		IV_Log("BANK_UPDATE: More Bank space has been rented, or has expired", 1,0,0);

		IVFrame_SetTable_Bank();
		
		if ( IV_IsSelectedTabCurrentPlayer() ) then
			PopulateBankInventory( g_CurrentPlayerName );
		end

		-- save latest changes to our global object
		IVFrame_SaveTable();
		return;
	end
end


function IV_IsSelectedTabCurrentPlayer()

	-- figure out if the currently selected tab is the currently logged in character
	if ( not g_CurrentPlayerName or g_CurrentPlayerName == "" ) then
		g_CurrentPlayerName = UnitName("player");
	end
	
	if ( g_IVSelectedCharTabName == g_CurrentPlayerName ) then
		return true;
	end

	return false;
end


function IVFrame_SelectCharTab( id )

	--IV_Log( "IVFrame_SelectCharTab: " .. id )

	-- Set the chosen tab to a selected state and unselect all other tabs
	local tabObj;

	for i = 1, IV_MAX_CHARS_PER_REALM do

		tabObj = getglobal( "IVFrameCharTab" .. i );
		local CurChar = tabObj:GetText();
		
		if ( i == id ) then
			UIPanelTab_SetActiveState( tabObj, true );
			g_IVSelectedCharTabName = CurChar;
			g_IVSelectedCharTabID = i;
			--IV_Log( "set g_IVSelectedCharTabName to " .. g_IVSelectedCharTabName );
		else
			UIPanelTab_SetActiveState( tabObj, false );
		end
	end

	--IV_Log( "Populating " .. g_IVSelectedCharTabName .. "'s inventory..." );
	
	-- Set Bank Tab to page 1
	IVFrameBankBackdropFrame.PageIndex = 1;
	IV_BankFrameTab_SetTabID( 1 );
	
	-- Set Backpack Tab to page 1
	IVFrameBackpackBackdropFrame.PageIndex = 1;
	IV_BackpackFrameTab_SetTabID( 1 );

	-- Populate the UI with items of the selected tab
	-- PopulateBankInventory( g_IVSelectedCharTabName ); -- This is done already in the function call above: IV_BankFrameTab_SetTabID( 1 );
	PopulateHouseChestInventory( g_IVSelectedCharTabName );
	PopulateHouseHangerInventory( g_IVSelectedCharTabName );
	--PopulateBackpackInventory( g_IVSelectedCharTabName ); == This is done already in the function call above: IV_BackpackFrameTab_SetTabID( 1 );
end


function IVFrameCharTab_OnClick( this, button )

	--IV_Log( "IVFrameCharTab_OnClick: " .. button )

	-- If there's a pop-up menu open already, close it
	if( IV_CharTabDropDown:IsVisible() )then
		ToggleDropDownMenu(IV_CharTabDropDown);
		IV_CharTabDropDown:Hide();
	end

	-- switch to the selected tab
	IVFrame_SelectCharTab( this:GetID() );

	if ( button == "RBUTTON" ) then
		UIDropDownMenu_SetAnchor(IV_CharTabDropDown, 0, 0, "BOTTOMLEFT", "TOPLEFT", this:GetName());
		ToggleDropDownMenu(IV_CharTabDropDown);
		IV_CharTabDropDown:Show();
	end
end

function IVFrameCharTab_OnLoad( this )

	--IV_Log( "IVFrameCharTab_OnLoad" );
	PanelTemplates_TabResize( this, 9.5/GetUIScale() );
	
	this:RegisterForClicks("LeftButton", "RightButton");
end


function PopulateBankInventory( CharacterName )

	--IV_Log( "PopulateBankInventory" );
	
	-- Initialize to first Tab if not initialized yet
	if ( not IVFrameBankBackdropFrame.PageIndex ) then
		IVFrameBankBackdropFrame.PageIndex = 1;
	end

	-- We only show 40 buttons for the Bank
	for i = 1, 40 do
		
		local itemButton = getglobal("IVItemButton"..i);
		itemButton.index = i;
		
		-- The Table supports all 200 slots, figure out which slot in the Bank table we're on
		local TableIndex = ( IVFrameBankBackdropFrame.PageIndex - 1 ) * 40 + i;
		
		if (g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].Bank[TableIndex] and
			g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].Bank[TableIndex].texture and
			g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].Bank[TableIndex].itemCount) then
			
			SetItemButtonTexture( itemButton, g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].Bank[TableIndex].texture);
			SetItemButtonCount( itemButton, g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].Bank[TableIndex].itemCount);
		else
			-- currently selected character may not have a House Chest, make sure we clear out whatever we're showing
			SetItemButtonTexture( itemButton, nil );
			SetItemButtonCount( itemButton, 0 );
		end
	end	
	
	--IV_Log( "end PopulateBankInventory" );
end


function PopulateBackpackInventory( CharacterName )

	--IV_Log( "PopulateBackpackInventory" );

	-- Initialize to first Tab if not initialized yet
	if ( not IVFrameBackpackBackdropFrame.PageIndex ) then
		IVFrameBackpackBackdropFrame.PageIndex = 1;
	end
	
	-- We only show 60 buttons for the Bank
	for i = 41, 100 do
	
		local itemButton = getglobal("IVItemButton"..i);	
		itemButton.index = i-40;

		-- The Table supports all 180 slots, figure out which slot in the Backpack table we're on
		local TableIndex = ( IVFrameBackpackBackdropFrame.PageIndex - 1 ) * 60 + (i-40);
	
		if (g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].Backpack[TableIndex] and
			g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].Backpack[TableIndex].texture and
			g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].Backpack[TableIndex].itemCount) then
		
			SetItemButtonTexture( itemButton, g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].Backpack[TableIndex].texture);
			SetItemButtonCount( itemButton, g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].Backpack[TableIndex].itemCount);
		else
			-- currently selected character may not have a House Chest, make sure we clear out whatever we're showing
			SetItemButtonTexture( itemButton, nil );
			SetItemButtonCount( itemButton, 0 );
		end
	end	
	
	--IV_Log( "end PopulateBackpackInventory" );
end


function PopulateHouseHangerInventory( CharacterName )

	--IV_Log( "PopulateHouseHangerInventory" );
	
	for i = 121, 130 do
	
		local itemButton = getglobal("IVItemButton"..i);	
		itemButton.index = i-120;
		
		if ( i < 129 and -- Clothes Rack
			g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].ClothesRack and
			g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].ClothesRack[i-120] and
			g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].ClothesRack[i-120].texture ) then		
			
			SetItemButtonTexture( itemButton, g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].ClothesRack[i-120].texture);
			
		elseif ( i > 128 and -- Weapon Rack (for the last 2)
				g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].WeaponRack and
				g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].WeaponRack[i-128] and
				g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].WeaponRack[i-128].texture ) then		
				
				SetItemButtonTexture( itemButton, g_InventoryViewerTable[g_AccountName][g_Realm][CharacterName].WeaponRack[i-128].texture);
		else
			-- currently selected character may not have a House Hanger or have an item in this slot , show the specialized texture for this slot
			local szSlot = "";
			
			if ( i == 121 ) then szSlot = "HeadSlot";
			elseif ( i == 122 ) then szSlot = "ShoulderSlot";
			elseif ( i == 123 ) then szSlot = "ChestSlot";
			elseif ( i == 124 ) then szSlot = "HandsSlot";
			elseif ( i == 125 ) then szSlot = "WaistSlot";
			elseif ( i == 126 ) then szSlot = "FeetSlot";
			elseif ( i == 127 ) then szSlot = "LegsSlot";
			elseif ( i == 128 ) then szSlot = "BackSlot";
			elseif ( i == 129 ) then szSlot = "MainHandSlot";
			elseif ( i == 130 ) then szSlot = "SecondaryHandSlot"; 
			end
			
			local txt = "interface/CharacterFrame/Equipment-" .. szSlot;
			SetItemButtonTexture( itemButton, txt );
		end
	end	
	
	--IV_Log( "end PopulateHouseHangerInventory" );
end


function IV_SetSelectedChest( PlayerName, DBID )

	if ( g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName] ) then
		g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].SelectedChestDBID = DBID;
	else
		IV_Log( "IV_SetSelectedChest Error: g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName] doesn't exist!", 1,0,0 );
	end	
end


function IV_GetPrevNextChest( PlayerName, CurDBID )

	--IV_Log("begin IV_GetPrevNextChest for " .. PlayerName );
	
	local PrevDBID = nil;
	local NextDBID = nil;
	local bFindPrev = true;
	
	if ( g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest ) then

		for DBID, ChestTable in pairs( g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest ) do
		
			if ( DBID ) then
			
				-- Previous Chest
				if ( bFindPrev and DBID ~= CurDBID ) then
					PrevDBID = DBID;
					--IV_Log("IV_GetPrevNextChest: PrevDBID = " ..DBID);
				end
				
				-- Current Chest
				if ( DBID == CurDBID ) then
					bFindPrev = false;
					--IV_Log("IV_GetPrevNextChest: CurDBID = " ..DBID);
				end
				
				-- Next Chest
				if ( not bFindPrev and DBID ~= CurDBID ) then
					NextDBID = DBID;
					--IV_Log("IV_GetPrevNextChest: NextDBID = " ..DBID);
					
					-- we're done here
					break;
				end
			end
		end
	else
		--IV_Log("IV_GetPrevNextChest: " .. PlayerName .. " doesn't have any chests.");
	end
	
	--IV_Log("end IV_GetPrevNextChest");
	
	return PrevDBID, NextDBID;
end


function IV_GetSelectedChest( PlayerName )

	--IV_Log( "start IV_GetSelectedChest" );
	
	--  If we don't have this player, we don't have a selected chest for him
	if ( not g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName] ) then
	
		--IV_Log( "end IV_GetSelectedChest nil,nil" .. PlayerName );
		return nil, nil;
	end
	
	local selected = nil;
	local ChestName = nil;
	
	-- See if this character had a chest previously selected, if so, populate that info
	if ( g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].SelectedChestDBID ) then
		
		selected = g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].SelectedChestDBID;
	
	-- else select the first chest we find in the table and populate the UI with that info
	elseif ( g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest ) then
	
		for DBID, ChestTable in pairs( g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest ) do
		
			if ( DBID ) then
				selected = DBID;
				IV_SetSelectedChest( PlayerName, selected );
				
				--IV_Log( "IV_GetSelectedChest: No selected DBID, using first one found: " .. selected );
				break;
			end
		end
	else
		--IV_Log( "IV_GetSelectedChest: No SelectedChestDBID, or .Chest for " .. g_AccountName.."/"..g_Realm.."/"..PlayerName );
	end
	
	if ( selected and
		 g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest and
		 g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest[selected] and
		 g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest[selected].Name ) then
		
		ChestName = g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest[selected].Name;
		--IV_Log( "IV_GetSelectedChest: " .. PlayerName .. " has Chest " .. ChestName.. "(" .. selected .. ") selected." );
		
		IVFrameBackdropHouseChest:SetText( ChestName .. " :" );
		--/run IV_SetSelectedChest( "Herbie", 99 ); PopulateHouseChestInventory("Herbie");
	else
		--IV_Log( "IV_GetSelectedChest: " .. PlayerName .. " has no chest.  Can't get/set selected chest." );
		local HouseChestText = getglobal("IVFrameBackdropHouseChest");
		if ( HouseChestText ) then
			HouseChestText:SetText( TEXT_HOUSE_CHEST );
		end
	end
	
	--IV_Log( "end IV_GetSelectedChest" );
	
	return selected, ChestName;
end


function PopulateHouseChestInventory( PlayerName )

	--IV_Log( "start PopulateHouseChestInventory" );
	
	local selected = IV_GetSelectedChest( PlayerName );
	
	for i = 101, 120 do

		local itemButton = getglobal("IVItemButton"..i);
		itemButton.index = i-100;

		if ( itemButton and selected and
							g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest and
							g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest[selected] and 
							g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest[selected][i-100] and 
							g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest[selected][i-100].texture and
							g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest[selected][i-100].itemCount ) then

			SetItemButtonTexture( itemButton, g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest[selected][i-100].texture);
			SetItemButtonCount( itemButton, g_InventoryViewerTable[g_AccountName][g_Realm][PlayerName].Chest[selected][i-100].itemCount);
		else
			-- currently selected character may not have a House Chest, make sure we clear out whatever we're showing
			SetItemButtonTexture( itemButton, nil );
			SetItemButtonCount( itemButton, 0 );
		end
	end	

	--IV_Log( "end PopulateHouseChestInventory" );
end





----------------------------------------------------------------------------------------------------------------------
--
--    InventoryViewerDropDown Menu
--
----------------------------------------------------------------------------------------------------------------------
function IV_CharTabDropDown_OnClick( button )

	--IV_Log( "IV_CharTabDropDown_OnClick: " .. button.value );

	-- Delete the specified character entry from our database
	if(  button.value == "DELETE" ) then

		-- You can't delete the currently logged in player
		if ( IV_IsSelectedTabCurrentPlayer() == true ) then
			IV_Log( "You can not remove yourself from the Inventory Viewer." );
			return;
		end

		-- Get the DisplayOrder number before wiping him out
		local DeletedOrderNum = g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName].DisplayOrder;
		--IV_Log("Deleted OrderNum: " .. DeletedOrderNum );

		-- Remove the specified character from our database
		IV_Log( "Deleting " .. g_IVSelectedCharTabName .. " from Inventory Viewer database..." );
		g_InventoryViewerTable[g_AccountName][g_Realm][g_IVSelectedCharTabName] = nil;

		-- Shift left all characters after this one in the order list and change the DisplayOrder for each character 
		-- that has one above this guy
		for i=DeletedOrderNum, IV_MAX_CHARS_PER_REALM do
			g_CharTabOrder[i] = g_CharTabOrder[i+1] or nil;
			g_CharTabOrder[i+1] = nil;

			if ( g_CharTabOrder[i] ) then
				g_InventoryViewerTable[g_AccountName][g_Realm][g_CharTabOrder[i]].DisplayOrder = i;
			else
				-- no more characters if we hit a nil
				break;
			end
		end

		IVFrame_SaveTable();

		-- Wipe out all the tabs so we can redo them
		for i=1, IV_MAX_CHARS_PER_REALM do
			local tab = getglobal("IVFrameCharTab"..i);
			tab:SetText("");
			tab:Hide();
		end

		-- Reset the tabs to match the updated database
		local MyTabId = IV_SetCharTabNames();
		
		-- Select the currently logged in players Tab,  Will also Populate the UI
		IVFrame_SelectCharTab( MyTabId );
	end
	
	-- Hide the popup menu as we're done with it
	--IV_CharTabDropDown:Hide();
end


function IV_CharTabDropDown_Show(this)
	
	--IV_Log( "IV_CharTabDropDown_Show" );

	local info = {};
	info.text 	= TEXT(C_DEL);
	info.notCheckable = 1;
	info.value	= "DELETE";
	info.func 	= IV_CharTabDropDown_OnClick;
	UIDropDownMenu_AddButton( info, 1 );
	
	info = {};
	info.text = TEXT(C_CANCEL);
	info.notCheckable = 1;
	info.value	= "CANCEL";
	info.func = IV_CharTabDropDown_OnClick;
	UIDropDownMenu_AddButton(info);
end


function IV_CharTabDropDown_OnLoad(this)

	--IV_Log( "** IV_CharTabDropDown_OnLoad **" );
	UIDropDownMenu_Initialize(this, IV_CharTabDropDown_Show, "MENU");
end


function CloseAllWindows()

	local isVisible = IVFrame:IsVisible();
	
	-- Close my window
	HideUIPanel(IVFrame);
	
	-- Call the original
	return orig_CloseAllWindows() or isVisible;
end


function IV_AppendTooltipData( tooltip, itemLink )

	--IV_Log( "IV_AppendTooltipData( " ..tooltip:GetName().. ", " .. itemLink .. " )" );
	
	if ( not g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].UpdateTooltips or
		 g_InventoryViewerTable[g_AccountName][INTERNAL_SETTINGS_TABLE].UpdateTooltips == 0 ) then
		 
		return;
	end
	
	-- Get text we want to insert
	local text = IV_GetListOfItems( itemLink );
	
	--IV_Log( "text:" .. text );

	if ( text and text ~= "" ) then
	
		-- Figure out the position to put the separator and set it
		local scale = GetUIScale();
		local Separator4 = getglobal( tooltip:GetName() .. "Separator4" );
		Separator4:ClearAllAnchors();
		Separator4:SetAnchor("TOP", "TOP", tooltip, 0, (tooltip:GetBottom()/scale)-(tooltip:GetTop()/scale)-(3.25/scale) );
		tooltip:AddLine("\n");
		
		-- Insert the text
		--IV_Log( "Appending tooltipText: " .. text );
		tooltip:AddLine( text );
		
		-- Set width of the separator after we've added text (which could change the size of the tooltip
		Separator4:SetWidth(tooltip:GetWidth()-20);
		Separator4:Show();
	end
end


function GameTooltipHyperLink:SetHyperLink( itemLink )

	-- Call original first, then add our stuff
	orig_GameTooltipHyperLinkSetHyperLink( self, itemLink );
	
	-- Append our stuff on the tooltip
	if ( not itemLink or itemLink == "" ) then
		return;
	end
	
	--IV_Log( "GameTooltipHyperLink:SetHyperLink( " .. itemLink .. " )" );
	
	IV_AppendTooltipData( self, itemLink );
end


function GameTooltip:SetHyperLink( itemLink )

	-- Call original first, then add our stuff
	orig_GameTooltipSetHyperLink( self, itemLink );
	
	-- Append our stuff on the tooltip
	if ( not itemLink or itemLink == "" ) then
		return;
	end
	
	--IV_Log( "GameTooltip:SetHyperLink( " .. itemLink .. " )" );
	
	IV_AppendTooltipData( self, itemLink );
end


function GameTooltip:SetBagItem( BagId )

	-- Call original first, then add our stuff
	orig_GameTooltipSetBagItem( self, BagId );
	
	-- Append our stuff on the tooltip
	local itemLink = GetBagItemLink( BagId );
	
	if ( not itemLink or itemLink == "" ) then
		return;
	end

	--IV_Log( "GameTooltip:SetBagItem( " .. BagId .. " )" );

	IV_AppendTooltipData( self, itemLink );
end


function GameTooltip:SetBankItem( BankId )

	-- Call original first, then add our stuff
	orig_GameTooltipSetBankItem( self, BankId );
	
	local itemLink = GetBankItemLink( BankId );
	
	-- Append our stuff on the tooltip
	if ( not itemLink or itemLink == "" ) then
		return;
	end

	--IV_Log( "GameTooltip:SetBankItem( " .. BankId .. " )" );

	IV_AppendTooltipData( self, itemLink );
end


function GameTooltip:SetAuctionBrowseItem( AuctionId )

	-- Call original first, then add our stuff
	orig_GameTooltipSetAuctionBrowseItem( self, AuctionId );
	
	-- Append our stuff on the tooltip
	local itemLink = GetAuctionBrowseItemLink( AuctionId );
	
	if ( not itemLink or itemLink == "" ) then
		return;
	end

	--IV_Log( "GameTooltip:SetAuctionBrowseItem( " .. AuctionId .. " )" );

	IV_AppendTooltipData( self, itemLink );
end


function GameTooltip:SetBootyItem( BootyId )

	-- Call original first, then add our stuff
	orig_GameTooltipSetBootyItem( self, BootyId );
	
	-- Append our stuff on the tooltip
	local itemLink = GetBootyItemLink( BootyId );
	
	if ( not itemLink or itemLink == "" ) then
		return;
	end

	--IV_Log( "GameTooltip:SetBootyItem( " .. BootyId .. " )" );

	IV_AppendTooltipData( self, itemLink );
end


function GameTooltip:SetHouseItem( ParentDBID, ButtonId )

	-- Call original first, then add our stuff
	orig_GameTooltipSetHouseItem( self, ParentDBID, ButtonId );
	
	-- Append our stuff on the tooltip
	local itemLink = Houses_GetItemLink( ParentDBID, ButtonId );
	
	if ( not itemLink or itemLink == "" ) then
		return;
	end

	--IV_Log( "GameTooltip:SetHouseItem( " .. ParentDBID .."/".. ButtonId .. " )" );

	IV_AppendTooltipData( self, itemLink );
end


function GameTooltip:SetStoreItem( tab, ButtonId )

	-- Call original first, then add our stuff
	orig_GameTooltipSetStoreItem( self, tab, ButtonId );
	
	-- Append our stuff on the tooltip
	local itemLink = "";

	--IV_Log( "GameTooltip:SetStoreItem( " .. tab .."/".. ButtonId .. " )" );
	
	if ( tab == "SELL" ) then
		itemLink = GetStoreSellItemLink( ButtonId );
	elseif ( tab == "BUYBACK" ) then
		itemLink = GetStoreBuyBackItemLink( ButtonId );
	end
	
	if ( not itemLink or itemLink == "" ) then
		return;
	end

	IV_AppendTooltipData( self, itemLink );
end


function IVFrameCharTab_OnDragStart(this)

	--IV_Log( "IVFrameCharTab_OnDragStart" );
	
	-- Make sure SHIFT key is down for switching the order of the tabs
	if ( not IsShiftKeyDown() ) then
		--IV_Log( "Press SHIFT when dragging the Inventory Viewer Tabs around." );
		return;
	end
	
	-- Save Src ID
	g_MoveSrcCharId = this:GetID();

	-- Start the dragging
	local DragTab = getglobal("IVFrameCharTabDrag");
	DragTab:SetText( getglobal(this:GetName()):GetText() );
	PanelTemplates_TabResize( DragTab, 9.5/GetUIScale() );
	UIPanelTab_SetActiveState( DragTab, true );
	DragTab:ClearAllAnchors();
	DragTab:SetAnchor( "CENTER", "CENTER", this , 0, 0 );
	DragTab:Show();
	DragTab:StartMoving();
end


function IVFrameCharTab_Insert( InsertPos )

	-- Bail out if the src id is not 1 to 8
	if ( not ( g_MoveSrcCharId > 0 and g_MoveSrcCharId < 9  and  InsertPos > 0 and InsertPos < 9 ) ) then
		--IV_Log("bad src id or insert position" );
		return false;
	end

	local SrcCharName = g_CharTabOrder[g_MoveSrcCharId];

	-- Src tab is to the right of Dst tab (insert before)
	if ( InsertPos < g_MoveSrcCharId ) then

		-- only shift the values within the range that is affected
		for i=g_MoveSrcCharId, InsertPos+1, -1 do
			--IV_Log( "moving (high to low)" .. i-1 .. " to " .. i );
			g_CharTabOrder[i] = g_CharTabOrder[i-1];
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CharTabOrder[i]].DisplayOrder = i;
		end

	-- Src tab is to the left of Dst tab (insert after)
	else
		-- only shift the values within the range that is affected
		for i=g_MoveSrcCharId, InsertPos-1 do
			--IV_Log( "moving (low to high) " .. i+1 .. " to " ..i );
			g_CharTabOrder[i] = g_CharTabOrder[i+1];
			g_InventoryViewerTable[g_AccountName][g_Realm][g_CharTabOrder[i]].DisplayOrder = i;
		end
	end

	-- now that we've shifted everything over, insert the src to the dst
	--IV_Log( "pasting " ..SrcCharName.. " at " ..InsertPos );
	g_CharTabOrder[InsertPos] = SrcCharName;
	g_InventoryViewerTable[g_AccountName][g_Realm][SrcCharName].DisplayOrder = InsertPos;

	-- we no longer need to know what that src tab was
	g_MoveSrcCharId = 0;

	return true;
end


function IVFrameCharTab_AbortDrag(this)

	-- Hide our Drag tab (stop dragging)
	local DragTab = getglobal("IVFrameCharTabDrag")
	DragTab:StopMovingOrSizing();
	DragTab:Hide();

	-- we no longer need to know what that src tab was
	g_MoveSrcCharId = 0;
end


function IVFrameCharTab_OnReceiveDrag(this)

	--IV_Log( "IVFrameCharTab_OnReceiveDrag" );

	local DstId = this:GetID();
	--IV_Log( "this:GetID: " .. DstId );
	
	-- Hide our Drag tab
	local DragTab = getglobal("IVFrameCharTabDrag")
	DragTab:StopMovingOrSizing();
	DragTab:Hide();

	-- Insert the selected tab to the destination position
	if ( not IVFrameCharTab_Insert( DstId ) ) then
		-- something failed, bail out
		--IV_Log( "something failed, bailing." );
		return;
	end

	-- Update our tabs with the character names in their new locations
	IV_SetCharTabNames();

	-- Write out our data to the INI file incase user quits
	IVFrame_SaveTable();

	-- Select the Tab of the src character
	IVFrame_SelectCharTab( DstId );

	-- Populate the UI with the src characters items
	local SrcCharName = g_CharTabOrder[DstId];
	PopulateBankInventory( SrcCharName );
	PopulateHouseChestInventory( SrcCharName );
	PopulateBackpackInventory( SrcCharName );
	
	--IV_Log( "Drag completE" );
end


function IV_MinimapButton_OnClick(this)

	if ( IVFrame:IsVisible() ) then
		HideUIPanel(IVFrame);
	else
		ShowUIPanel(IVFrame);
	end
end

function IV_MinimapButton_OnEnter(this)

end

function IV_MinimapButton_OnLeave(this)

end


function IVFrame_HouseChestLeftArrow_OnClick(this)

	--IV_Log( "IVFrame_HouseChestLeftArrow_OnClick" );
	
	local prevChest, nextChest = IV_GetPrevNextChest( g_IVSelectedCharTabName, IV_GetSelectedChest( g_IVSelectedCharTabName ) );

	if ( prevChest ) then
		IV_SetSelectedChest( g_IVSelectedCharTabName, prevChest );
		PopulateHouseChestInventory( g_IVSelectedCharTabName );	
		
		--IV_Log( "Prev Chest: " .. prevChest );
	else
		--IV_Log( "Prev Chest not found." );
	end
end


function IVFrame_HouseChestRightArrow_OnClick(this)

	--IV_Log( "IVFrame_HouseChestRightArrow_OnClick" );
	
	local prevChest, nextChest = IV_GetPrevNextChest( g_IVSelectedCharTabName, IV_GetSelectedChest( g_IVSelectedCharTabName ) );

	if ( nextChest ) then
		IV_SetSelectedChest( g_IVSelectedCharTabName, nextChest );
		PopulateHouseChestInventory( g_IVSelectedCharTabName );	
		
		--IV_Log( "Next Chest: " .. nextChest );
	else
		--IV_Log( "Next Chest not found." );
	end
end


function IV_BankFrameTab_SetTabID( id )

	--IV_Log( "IV_BankFrameTab_SetTabID: " .. id );
	
	local tabObj;
	for i = 1, 5, 1 do
		tabObj = getglobal( "IVFrameBankTab" .. i );
		if( i == id )then
			UIPanelTab_SetActiveState( tabObj, true );
		else
			UIPanelTab_SetActiveState( tabObj, false );
		end
	end
	
	-- Update the UI
	PopulateBankInventory( g_IVSelectedCharTabName, id );
end


function IV_BankFrameTab_OnClick( this )

	IVFrameBankBackdropFrame.PageIndex = this:GetID();
	IV_BankFrameTab_SetTabID( this:GetID() );
end


function IV_BackpackFrameTab_SetTabID( id )

	--IV_Log( "IV_BackpackFrameTab_SetTabID: " .. id );
	
	-- Tab 1 is page 1/2, Tab 2 is page 3/4, Tab 3 is page 5/6
	local tabObj;
	for i = 1, 3, 1 do
		tabObj = getglobal( "IVFrameBackpackTab" .. i );
		if( i == id )then
			UIPanelTab_SetActiveState( tabObj, true );
		else
			UIPanelTab_SetActiveState( tabObj, false );
		end
	end
	
	-- Update the UI
	PopulateBackpackInventory( g_IVSelectedCharTabName, id );
end


function IV_BackpackFrameTab_OnClick( this )

	IVFrameBackpackBackdropFrame.PageIndex = this:GetID();
	IV_BackpackFrameTab_SetTabID( this:GetID() );
end
